#include <stdio.h>
#include <string.h>
#include <usb.h>
#include <unistd.h>

#define kHZ 1000
#define BUFFER_LEN 64

#define CHUNK_BUFFER_SIZE 64

typedef unsigned char    __u8;
typedef unsigned char	u_int8_t;

typedef struct DATA_PACKET
{
    __u8 length;
    __u8 data[BUFFER_LEN-1];
} DATA_PACKET;

typedef struct CHUNK
{
	BYTE	id;
	WORD	value;
	BYTE	time;
} __attribute__ ((packed)) CHUNK;


typedef union DATA_KOMBI_PACKET
{
	BYTE _byte [CHUNK_BUFFER_SIZE];
	struct
	{
		WORD 	length;
		CHUNK 	data[(CHUNK_BUFFER_SIZE - 1)/sizeof(CHUNK)];
	};
} __attribute ((packed)) DATA_KOMBI_PACKET;

extern int getInfoForUSB (unsigned char *, int);

struct usb_info
  {
        struct usb_device        *udev;
        struct usb_dev_handle    *dev;
        size_t                    bulk_size;
        __u8                      bulk_in_endpointAddr;
        __u8                      bulk_out_endpointAddr;
        int                       bulk_in_interface;
        int                       bulk_out_interface;
  };


int send_command(struct usb_info *ptr, unsigned char* buffer, int length);
int get_command(struct usb_info *ptr, unsigned char* buffer, int length);

struct usb_info* find_device();
void memdump(DATA_PACKET *b);
void memdump_test(DATA_KOMBI_PACKET *b);


int main(int argc, char *argv[])
  {
    unsigned char src[BUFFER_LEN];
    unsigned char dst[BUFFER_LEN];
    CHUNK c;

    int i,j;
    int length;

//    length = getInfoForUSB(src, BUFFER_LEN);
//
//    return 1;

    size_t sz = sizeof (BYTE);
    sz = sizeof(WORD);
    sz = sizeof(CHUNK);

    DATA_KOMBI_PACKET dkp;
//    dkp.length = 5;
//    dkp.data.time = 2;
//    dkp.data.value = 1;

    struct usb_info* usb_ptr = NULL;

    /* find the usb device */
    usb_ptr = find_device();

    if (! usb_ptr)
      {
        printf("Cannot find device\n");
        exit(1);
      }

    /* open the usb device */
    usb_ptr->dev = usb_open(usb_ptr->udev);

    while ((length = getInfoForUSB (src, BUFFER_LEN)) != 0) {
    	/* Send the buffer contents to the usb device */
		if (send_command(usb_ptr,src,length) <= 0)
		  {
			printf("ERROR sending bytes\n");
			exit(1);
		  }

		/* Receive the buffer contents from the usb device */
		if (get_command(usb_ptr,dst,64/*length*/) <= 0)
		  {
			printf("ERROR reading bytes\n");
			exit(1);
		  }

    }


//    for (length=8; length < (usb_ptr->bulk_size-8); length++)
//      {
//
//        /* Populate the source buffer with dummy data */
//        for (i=0; i<length; i++)
//            src[i] = i;
//
//        /* Send the buffer contents to the usb device */
//        if (send_command(usb_ptr,src,length) <= 0)
//          {
//            printf("ERROR sending bytes\n");
//            exit(1);
//          }
//
//        /* Receive the buffer contents from the usb device */
//        if (get_command(usb_ptr,dst,length) <= 0)
//          {
//            printf("ERROR reading bytes\n");
//            exit(1);
//          }
//
//        /* Verify the dst buffer is the reverse of the src buffer */
//        for (j=0; j<length; j++)
//          {
//            if ( ! (src[j] == dst[length-j-1]) )
//              {
//                printf("ERROR: src and dst don't match\n");
//                exit(1);
//              }
//          }
//        printf("\n");
//      }

    (void) usb_close(usb_ptr->dev);
    printf("SUCCESS\n");
    return 0;
  }

struct usb_info* find_device()
  {
    struct usb_bus *bus;
    struct usb_config_descriptor *config;
    struct usb_interface_descriptor *interface;
    struct usb_endpoint_descriptor *endpoint;

    struct usb_info *usb_ptr;

    usb_ptr = (struct usb_info*) malloc(sizeof(struct usb_info));

    if (!usb_ptr) return NULL;

    memset(usb_ptr,0,sizeof(struct usb_info));

    int i,j,k;

    usb_set_debug(50);

    usb_init();

    usb_find_busses();
    usb_find_devices();

    /* this seems fairly complicated */
    for (bus = usb_get_busses (); bus; bus = bus->next)
      {
          struct usb_device *dev;

          for (dev = bus->devices; dev; dev = dev->next)
            {
              /* We are searching for a device with
               *   idVendor  =  0x04d8
               *   idProduct = 0x000c
               */
              if ((dev->descriptor.idVendor == 0x04d8) && (dev->descriptor.idProduct == 0x000c))
                {
                  printf("descriptor.idVendor = %x\n",dev->descriptor.idVendor);
                  printf("descriptor.idProduct = %x\n",dev->descriptor.idProduct);

                 usb_ptr->udev = dev;

                 for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
                   {
                     config = &dev->config[i];
                     printf(" dev->config[%d]\n",i);

                     for (j = 0; j < config->bNumInterfaces; j++)
                       {
                         printf(" config->interface[%d]\n",j);
                         interface = config->interface[j].altsetting;

                         printf("    bInterfaceNumber:   %d\n", interface->bInterfaceNumber);
                         printf("    bAlternateSetting:  %d\n", interface->bAlternateSetting);
                         printf("    bNumEndpoints:      %d\n", interface->bNumEndpoints);
                         printf("    bInterfaceClass:    %d\n", interface->bInterfaceClass);
                         printf("    bInterfaceSubClass: %d\n", interface->bInterfaceSubClass);
                         printf("    bInterfaceProtocol: %d\n", interface->bInterfaceProtocol);
                         printf("    iInterface:         %d\n", interface->iInterface);

                         for (k = 0; k < interface->bNumEndpoints; k++)
                           {
                        	 u_int8_t ep_type;
                             endpoint = &interface->endpoint[k];
                             printf("Found End Point.\n");

                             /* We are searching for an IN and OUT bulk endpoint */
                             ep_type = (USB_ENDPOINT_TYPE_MASK & endpoint->bmAttributes);

//                             if (ep_type == USB_ENDPOINT_TYPE_BULK)
//                               {
                                 printf("Found Bulk End Point. ep_type = %d\n", ep_type);
                                 if (USB_ENDPOINT_DIR_MASK & endpoint->bEndpointAddress)
                                   {
                                     printf("Found Bulk End Point: IN\n");
                                     usb_ptr->bulk_size = endpoint->wMaxPacketSize;
                                     usb_ptr->bulk_in_endpointAddr = endpoint->bEndpointAddress;
                                     usb_ptr->bulk_in_interface = interface->bInterfaceNumber;
                                     printf("\tbEndpointAddress: 0x%x\n",usb_ptr->bulk_in_endpointAddr);
                                     printf("\tbInterfaceNumber: %d\n",usb_ptr->bulk_in_interface);
                                     printf("\twMaxPacketSize: %d\n",usb_ptr->bulk_size);
                                   }
                                 else
                                   {
                                     printf("Found Bulk End Point: OUT\n");
                                     usb_ptr->bulk_out_endpointAddr = endpoint->bEndpointAddress;
                                     usb_ptr->bulk_out_interface = interface->bInterfaceNumber;
                                     printf("\tbEndpointAddress: 0x%x\n",usb_ptr->bulk_out_endpointAddr);
                                     printf("\tbInterfaceNumber: %d\n",usb_ptr->bulk_out_interface);
                                   }

//                               }

                           }

                       }
                   }

               }
           }

      }

    if (! usb_ptr->bulk_size) return NULL;
    if (! usb_ptr->bulk_in_endpointAddr) return NULL;
    if (! usb_ptr->bulk_out_endpointAddr) return NULL;

    return usb_ptr;
  } // end function



int send_command(struct usb_info *ptr, unsigned char* src, int length)
  {
    int retval,i;
    DATA_PACKET packet;

    memset(&packet,0,sizeof(DATA_PACKET));

    if(usb_set_configuration(ptr->dev, 1) < 0)
        {
          printf("error: setting config 1 failed\n");
          usb_close(ptr->dev);
          return 0;
        }

//      if(usb_claim_interface(dev, 0) < 0)
//        {
//          printf("error: claiming interface 0 failed\n");
//          usb_close(dev);
//          return 0;
//        }


    retval = usb_claim_interface(ptr->dev, ptr->bulk_out_interface);
    if (retval < 0)
      {
        printf("\n\tCannot claim interface: %s\n\n",strerror(-retval));
        return -1;
      }

    /* copy the data into our packet */
    packet.length = length;
    for (i=0; i<length; i++)
      packet.data[i] = src[i];

    memdump(&packet);

    retval = usb_bulk_write(
               ptr->dev,
               ptr->bulk_out_endpointAddr,
               (char*)&packet,
               packet.length + 1,
               5 * kHZ );


    if (retval < 0)
        printf("ERROR: Usb write retval = %d; %s\n", retval, strerror(-retval));
    else
      printf("- Wrote %d bytes\n", retval);

    return retval;
  }



int get_command(struct usb_info *ptr, unsigned char* dst, int length)
  {
    int retval,i;
    //DATA_PACKET packet;
    DATA_KOMBI_PACKET packet;
    retval = usb_claim_interface(ptr->dev, ptr->bulk_in_interface);
    if (retval < 0)
      {
        printf("\n\tCannot claim interface: %s\n\n",strerror(-retval));
        return -1;
      }

    memset(dst,0,sizeof(unsigned char)*length);
//    memset(&packet,0,sizeof(DATA_PACKET));
    memset(&packet,0,sizeof(DATA_KOMBI_PACKET));

    retval = usb_bulk_read(
               ptr->dev,
               ptr->bulk_in_endpointAddr,
               (char*)&packet,
               length+1,
               10 * kHZ );

    if (retval < 0)
        printf("ERROR: Usb read retval = %d; %s\n", retval, strerror(-retval));
    else
      printf("Read %d bytes\n", retval);

    memdump_test(&packet);

    //for (i=0; i<length; i++)
    //  dst[i] = packet.data[i];

    return(retval);
  }

void memdump_test (DATA_KOMBI_PACKET *b)
{
	printf ("memdump_test: [%d]\n",b->length);
	for (CHUNK *pc=b->data; pc != &b->data[b->length]; ++pc)
		printf ("    >> id = %d, value = %d, time = %d;\n", pc->id, pc->value, pc->time);
	printf ("\n");
}


void memdump(DATA_PACKET *b)
  {
    int i;
    printf("memdump: [%d][",b->length);
    for (i=0; i<b->length; i++)
      printf(" 0x%x",b->data[i]);
    printf("]\n");
  }
